package gui;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;

public class InvocadorDeMetodos 
{
	private DeclaracaoDeMetodo declaracaoDoMetodoASerInvocado;
	private Object objetoComMetodoASerInvocado;
	
	public InvocadorDeMetodos(DeclaracaoDeMetodo declaracaoMetodo, Object objetoComMetodo)
	{
		this.declaracaoDoMetodoASerInvocado = declaracaoMetodo;
		this.objetoComMetodoASerInvocado = objetoComMetodo;
	}
	
	@SuppressWarnings("rawtypes")
	public Object invocarMetodo(LinkedList<String> valoresDosParametros) throws ValorInvalidoParaParametroException
	{
		Class[] tiposParametrosDeEntrada = 
				this.declaracaoDoMetodoASerInvocado.getTiposDosParametrosDeEntrada();
		int quantosParametrosDeEntrada = tiposParametrosDeEntrada.length;
		Object[] objetosQueVouUsarParaInvocarMetodo = new Object[quantosParametrosDeEntrada];
		int percorredorObjetosQueVouUsarParaInvocarMetodo = 0;
		
		for(int i = 0; i < tiposParametrosDeEntrada.length; i++)
		{
			Class umParametro = tiposParametrosDeEntrada[i];
			String valorParaEsseParametro = valoresDosParametros.get(i);
			try
			{
				Object objetoQueVouUsarParaInvocarOMetodo =
						converterValorDoParametroEmObjeto(umParametro, valorParaEsseParametro);
				objetosQueVouUsarParaInvocarMetodo
				[percorredorObjetosQueVouUsarParaInvocarMetodo] =
													objetoQueVouUsarParaInvocarOMetodo;
				percorredorObjetosQueVouUsarParaInvocarMetodo =
						percorredorObjetosQueVouUsarParaInvocarMetodo + 1;
			}
			catch(ValorInvalidoParaParametroException e)
			{
				//usuario passou algum valor para um parametro que nao devia
				throw new ValorInvalidoParaParametroException();
			}
		}
		
		Method metodo = this.declaracaoDoMetodoASerInvocado.getMetodoAssociado();
		
		try 
		{
			if(tiposParametrosDeEntrada.length == 0)
			{
				//o metodo nao tem parametros de entrada. Pode invocar ser nada mesmo
				Object retornoDoMetodo = metodo.invoke(this.objetoComMetodoASerInvocado);
				return retornoDoMetodo;
			}
			else
			{
				Object retornoDoMetodo = 
						metodo.invoke(this.objetoComMetodoASerInvocado,
												objetosQueVouUsarParaInvocarMetodo);
				return retornoDoMetodo;
			}
		} 
		catch (IllegalAccessException e) 
		{
			e.printStackTrace();
			return null;
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			return null;
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	
	/*dado um valor para um parametro em String, 
	 * esse valor sera convertido para um objeto com o mesmo tipo de tipoDeUmParametro*/
	@SuppressWarnings("rawtypes")
	private Object converterValorDoParametroEmObjeto(Class tipoDeUmParametro,
					String valorParaEsteParametro) throws ValorInvalidoParaParametroException
	{
		if(ChecaTipoDeClasse.classeEhString(tipoDeUmParametro) == true)
		{
			return valorParaEsteParametro;
		}
		else if(ChecaTipoDeClasse.classeEhBoolean(tipoDeUmParametro) == true)
		{
			try
			{
				boolean valorEmBooleano = Boolean.valueOf(valorParaEsteParametro);
				return valorEmBooleano;
			}
			catch(Exception e)
			{
				/*se der algum erro na conversao da string para objeto, eh porque o usuario
				 * informou um valor invalida para o parametro do metodo. O metodo nao poderah
				 * ser invocado com este valor dado como entrada*/
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhByte(tipoDeUmParametro) == true)
		{
			try
			{
				byte valorEmByte = Byte.valueOf(valorParaEsteParametro);
				return valorEmByte;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhChar(tipoDeUmParametro) == true)
		{
			try
			{
				char valorEmChar = valorParaEsteParametro.charAt(0);
				return valorEmChar;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhDouble(tipoDeUmParametro) == true)
		{
			try
			{
				Double valorEmDouble = Double.valueOf(valorParaEsteParametro);
				return valorEmDouble;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhFloat(tipoDeUmParametro) == true)
		{
			try
			{
				Float valorEmFloat = Float.valueOf(valorParaEsteParametro);
				return valorEmFloat;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhInteiro(tipoDeUmParametro) == true)
		{
			try
			{
				int valorEmInt = Integer.valueOf(valorParaEsteParametro);
				return valorEmInt;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhLong(tipoDeUmParametro) == true)
		{
			try
			{
				Long valorEmLong = Long.valueOf(valorParaEsteParametro);
				return valorEmLong;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else if(ChecaTipoDeClasse.classeEhShort(tipoDeUmParametro) == true)
		{
			try
			{
				Short valorEmShort = Short.valueOf(valorParaEsteParametro);
				return valorEmShort;
			}
			catch(Exception e)
			{
				throw new ValorInvalidoParaParametroException();
			}
		}
		else
		{
			throw new ValorInvalidoParaParametroException(); //tambem nao trataremos tipos nao primitivos
		}
	}

}
